package com.hyl.gulimall.ware.config;

import com.hyl.gulimall.ware.entity.WareInfoEntity;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author hyl_marco
 * @data 2022/5/24 - 22:10
 */
@Configuration
public class MyRabbitMQConfig {
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 定制RabbitTemplate,实现消息的事务
     * @PostConstruct 注解指在该config对象创建完成之后再执行这个方法
     * 1. 服务收到消息就回调
     *      i. spring.rabbitmq.publisher-confirms=true
     *      ii. 设置确认回调 ConfirmCallback
     * 2. 消息正确抵达队列进行回调
     *      #消息到达队列就的确认 spring.rabbitmq.publisher-returns=true
     *      #到达队列，以异步的方式优先回调 spring.rabbitmq.template.mandatory=true
     * 3. 消费端确认（只有确认抵达了消费端，才能删除该消息）
     *      i. 默认是自动确认的，只要消息接受到，客户端会自动确认，服务端会移除这个消息
     *          问题：如果收到很多消息，还没处理完消费者宕机了，消息丢失
     *          解决：手动进行确认,只要没有告诉MQ确认，就一直是unacked
     *          spring.rabbitmq.listener.simple.acknowledge-mode=manual
     *          channel.basicAck(deliveryTag，false)签收：业务成功
     *          channel.basicNack()拒签：业务失败
     *
     */
//    @PostConstruct
//    public void initRabbitTemplate(){
//        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
//
//            /**
//             * 只要消息抵达服务器，就算成功
//             * @param correlationData 当前消息唯一关联的数据（位移id）
//             * @param b 消息是否收到
//             * @param s 失败原因
//             */
//            @Override
//            public void confirm(CorrelationData correlationData, boolean b, String s) {
//                System.out.println("confirm...");
//            }
//        });
//
//        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
//            /**
//             * 只要消息没有投递给指定的队列，就触发这个失败回调
//             * @param message 投递失败的消息
//             * @param i 回复的状态码
//             * @param s 回复的文本内容
//             * @param s1 该消息的交换机
//             * @param s2 该消息的路由键
//             */
//            @Override
//            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
//
//            }
//        });
//    }


    /**
     * 仓库服务消息队列组件的创建
     */
    @Bean
    public Exchange stockEventExchange() {
        return new TopicExchange("stock-event-exchange", true, false);
    }

    @Bean
    public Queue stockReleaseStockQueue() {
        return new Queue("stock.release.stock.queue", true, false, false);
    }

    //延时队列
    @Bean
    public Queue stockDelayQueue() {
        Map<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange", "stock-event-exchange");
        arguments.put("x-dead-letter-routing-key", "stock.release");
        arguments.put("x-message-ttl", 120000);
        return new Queue("stock.delay.queue", true, false, false, arguments);
    }

    @Bean
    public Binding stockLockedBinding() {
        return new Binding("stock.release.stock.queue",
                Binding.DestinationType.QUEUE,
                "stock-event-exchange",
                "stock.release.#",
                null);
    }

    @Bean
    public Binding stockReleaseBinding() {
        return new Binding("stock.delay.queue",
                Binding.DestinationType.QUEUE,
                "stock-event-exchange",
                "stock.locked",
                null);
    }

    //第一次监听消息时，idea会连接rabbitMQ,此时才会创建rdbbitMQ中没有的队列、交换机和绑定关系
    //如果需要修改rabbitMQ中已存在的队列交换机,需要先删除，然后再次创建
//    @RabbitListener(queues = "stock.release.stock.queue")
//    public void listener(WareInfoEntity entity, Channel channel, Message msg) throws IOException {
//        System.out.println("收到过期的订单信息:准备关闭订单" + entity.getId());
//        channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
//    }
}
